/* $Id: Preferences.java 52 2010-01-11 01:42:30Z aaron.day $ */
package org.crazydays.gameplan;


import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.configuration.CompositeConfiguration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.FileConfiguration;
import org.apache.commons.configuration.MapConfiguration;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.configuration.SystemConfiguration;
import org.apache.commons.configuration.reloading.ReloadingStrategy;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Preferences
 */
public final class Preferences
    extends CompositeConfiguration
    implements FileConfiguration
{
    /** logger */
    protected final static Logger LOGGER =
        LoggerFactory.getLogger(Preferences.class);

    /** properties filename */
    public final static String PROPERTIES_FILENAME = "gameplan.properties";

    /** file configuration */
    protected FileConfiguration userConfiguration;

    /**
     * Preferences constructor.
     */
    public Preferences()
    {
        initialize();
    }

    /**
     * Initialize preferences.
     */
    protected void initialize()
    {
        clear();
        initializeSystemConfiguration();
        initializeEnvironmentConfiguration();
        initializeUserPropertiesConfiguration();
        initializeApplicationPropertiesConfiguration();
    }

    /**
     * Initialize system configuration.
     */
    protected void initializeSystemConfiguration()
    {
        addConfiguration(new SystemConfiguration());
    }

    /**
     * Initialize environment configuration.
     */
    protected void initializeEnvironmentConfiguration()
    {
        // need to add a copy of the system
        // environment, because is it immutable
        Map<String, String> environment =
            new HashMap<String, String>(System.getenv());

        addConfiguration(new MapConfiguration(environment));
    }

    /**
     * Initialize user properties configuration.
     */
    protected void initializeUserPropertiesConfiguration()
    {
        File userPropertiesFile =
            new File(System.getProperty("user.home"), '.' + PROPERTIES_FILENAME);

        if (!userPropertiesFile.isFile() || !userPropertiesFile.exists()) {
            LOGGER.warn("Unable to locate user properties");

            // attempt to create properties file
            try {
                userPropertiesFile.createNewFile();
            } catch (IOException e) {
                LOGGER.warn("Unable to create user properties", e);
            }
        }

        try {
            userConfiguration = new PropertiesConfiguration(userPropertiesFile);
            addConfiguration(userConfiguration);
        } catch (ConfigurationException e) {
            LOGGER.warn("Unable to load user properties", e);
        }

    }

    /**
     * Initialize application properties configurations.
     */
    protected void initializeApplicationPropertiesConfiguration()
    {
        URL url =
            Thread.currentThread().getContextClassLoader().getResource(
                PROPERTIES_FILENAME);

        if (url == null) {
            LOGGER.warn("Unable to load application properties");
            return;
        }

        try {
            addConfiguration(new PropertiesConfiguration(url));
        } catch (ConfigurationException e) {
            LOGGER.warn("Unable to load application properties", e);
        }
    }

    /**
     * Set property value.
     * 
     * @param key Key
     * @param value Value
     * @see org.apache.commons.configuration.AbstractConfiguration#setProperty(String,Object)
     */
    @Override
    public void setProperty(String key, Object value)
    {
        if (userConfiguration == null) {
            throw new IllegalStateException("userConfiguration == null");
        }

        // clear the all of the other properties
        // so that on a live system environment and
        // system properties do not override the
        // new value which is going to be saved
        clearProperty(key);

        userConfiguration.setProperty(key, value);
    }

    /**
     * Set reloading strategy.
     * 
     * @param strategy Strategy
     * @see FileConfiguration#setReloadingStrategy(ReloadingStrategy)
     */
    @Override
    public void setReloadingStrategy(ReloadingStrategy strategy)
    {
        if (userConfiguration == null) {
            throw new IllegalStateException("userConfiguration == null");
        }

        userConfiguration.setReloadingStrategy(strategy);
    }

    /**
     * Get reloading strategy.
     * 
     * @return Reloading strategy
     * @see FileConfiguration#getReloadingStrategy()
     */
    @Override
    public ReloadingStrategy getReloadingStrategy()
    {
        if (userConfiguration == null) {
            throw new IllegalStateException("userConfiguration == null");
        }

        return userConfiguration.getReloadingStrategy();
    }

    /**
     * Set auto save.
     * 
     * @param autoSave Save
     * @see FileConfiguration#setAutoSave(boolean)
     */
    @Override
    public void setAutoSave(boolean autoSave)
    {
        if (userConfiguration == null) {
            throw new IllegalStateException("userConfiguration == null");
        }

        userConfiguration.setAutoSave(autoSave);
    }

    /**
     * Is auto save.
     * 
     * @return Is auto save
     * @see FileConfiguration#isAutoSave()
     */
    @Override
    public boolean isAutoSave()
    {
        if (userConfiguration == null) {
            throw new IllegalStateException("userConfiguration == null");
        }

        return userConfiguration.isAutoSave();
    }

    /**
     * Set encoding.
     * 
     * @param encoding Encoding
     * @see FileConfiguration#setEncoding(String)
     */
    @Override
    public void setEncoding(String encoding)
    {
        if (userConfiguration == null) {
            throw new IllegalStateException("userConfiguration == null");
        }

        userConfiguration.setEncoding(encoding);
    }

    /**
     * Get encoding.
     * 
     * @return Encoding
     * @see FileConfiguration#getEncoding()
     */
    @Override
    public String getEncoding()
    {
        if (userConfiguration == null) {
            throw new IllegalStateException("userConfiguration == null");
        }

        return userConfiguration.getEncoding();
    }

    /**
     * Set base path.
     * 
     * @param basePath Base path
     * @see FileConfiguration#setBasePath(String)
     */
    @Override
    public void setBasePath(String basePath)
    {
        if (userConfiguration == null) {
            throw new IllegalStateException("userConfiguration == null");
        }

        userConfiguration.setBasePath(basePath);
    }

    /**
     * Get base path.
     * 
     * @return Base path
     * @see FileConfiguration#getBasePath()
     */
    @Override
    public String getBasePath()
    {
        if (userConfiguration == null) {
            throw new IllegalStateException("userConfiguration == null");
        }

        return userConfiguration.getBasePath();
    }

    /**
     * Set filename.
     * 
     * @param fileName Filename
     * @see FileConfiguration#setFileName(String)
     */
    @Override
    public void setFileName(String fileName)
    {
        if (userConfiguration == null) {
            throw new IllegalStateException("userConfiguration == null");
        }

        userConfiguration.setFileName(fileName);
    }

    /**
     * Get filename.
     * 
     * @return Filename
     * @see FileConfiguration#getFileName()
     */
    @Override
    public String getFileName()
    {
        if (userConfiguration == null) {
            throw new IllegalStateException("userConfiguration == null");
        }

        return userConfiguration.getFileName();
    }

    /**
     * Set file.
     * 
     * @param file File
     * @see FileConfiguration#setFile(File)
     */
    @Override
    public void setFile(File file)
    {
        if (userConfiguration == null) {
            throw new IllegalStateException("userConfiguration == null");
        }

        userConfiguration.setFile(file);
    }

    /**
     * Get file.
     * 
     * @return File
     * @see FileConfiguration#getFile()
     */
    @Override
    public File getFile()
    {
        if (userConfiguration == null) {
            throw new IllegalStateException("userConfiguration == null");
        }

        return userConfiguration.getFile();
    }

    /**
     * Set URL.
     * 
     * @param url URL
     * @see FileConfiguration#setURL(URL)
     */
    @Override
    public void setURL(URL url)
    {
        if (userConfiguration == null) {
            throw new IllegalStateException("userConfiguration == null");
        }

        userConfiguration.setURL(url);
    }

    /**
     * Get URL.
     * 
     * @return URL
     * @see FileConfiguration#getURL()
     */
    @Override
    public URL getURL()
    {
        if (userConfiguration == null) {
            throw new IllegalStateException("userConfiguration == null");
        }

        return userConfiguration.getURL();
    }

    /**
     * Load.
     * 
     * @throws ConfigurationException
     * @see FileConfiguration#load()
     */
    @Override
    public void load()
        throws ConfigurationException
    {
        initialize();
    }

    /**
     * Reload.
     * 
     * @see FileConfiguration#reload()
     */
    @Override
    public void reload()
    {
        initialize();
    }

    /**
     * Save.
     * 
     * @throws ConfigurationException
     * @see FileConfiguration#save()
     */
    @Override
    public void save()
        throws ConfigurationException
    {
        if (userConfiguration == null) {
            throw new IllegalStateException("userConfiguration == null");
        }

        userConfiguration.save();
    }

    /**
     * Load.
     * 
     * @param fileName Filename
     * @throws ConfigurationException
     * @see FileConfiguration#load(String)
     */
    @Override
    public void load(String fileName)
        throws ConfigurationException
    {
        if (userConfiguration == null) {
            throw new IllegalStateException("userConfiguration == null");
        }

        userConfiguration.load(fileName);
    }

    /**
     * Save.
     * 
     * @param fileName Filename
     * @throws ConfigurationException
     * @see FileConfiguration#save(String)
     */
    @Override
    public void save(String fileName)
        throws ConfigurationException
    {
        if (userConfiguration == null) {
            throw new IllegalStateException("userConfiguration == null");
        }

        userConfiguration.save(fileName);
    }

    /**
     * Load.
     * 
     * @param file File
     * @throws ConfigurationException
     * @see FileConfiguration#load(File)
     */
    @Override
    public void load(File file)
        throws ConfigurationException
    {
        if (userConfiguration == null) {
            throw new IllegalStateException("userConfiguration == null");
        }

        userConfiguration.load(file);
    }

    /**
     * Save.
     * 
     * @param file File
     * @throws ConfigurationException
     * @see FileConfiguration#save(File)
     */
    @Override
    public void save(File file)
        throws ConfigurationException
    {
        if (userConfiguration == null) {
            throw new IllegalStateException("userConfiguration == null");
        }

        userConfiguration.save(file);
    }

    /**
     * Load.
     * 
     * @param url URL
     * @throws ConfigurationException
     * @see FileConfiguration#load(URL)
     */
    @Override
    public void load(URL url)
        throws ConfigurationException
    {
        if (userConfiguration == null) {
            throw new IllegalStateException("userConfiguration == null");
        }

        userConfiguration.load(url);
    }

    /**
     * Save.
     * 
     * @param url URL
     * @throws ConfigurationException
     * @see FileConfiguration#save(URL)
     */
    @Override
    public void save(URL url)
        throws ConfigurationException
    {
        if (userConfiguration == null) {
            throw new IllegalStateException("userConfiguration == null");
        }

        userConfiguration.save(url);
    }

    /**
     * Load.
     * 
     * @param in InputStream
     * @throws ConfigurationException
     * @see FileConfiguration#load(InputStream)
     */
    @Override
    public void load(InputStream in)
        throws ConfigurationException
    {
        if (userConfiguration == null) {
            throw new IllegalStateException("userConfiguration == null");
        }

        userConfiguration.load(in);
    }

    /**
     * Save.
     * 
     * @param out OutputStream
     * @throws ConfigurationException
     * @see FileConfiguration#save(OutputStream)
     */
    @Override
    public void save(OutputStream out)
        throws ConfigurationException
    {
        if (userConfiguration == null) {
            throw new IllegalStateException("userConfiguration == null");
        }

        userConfiguration.save(out);
    }

    /**
     * Load.
     * 
     * @param in InputStream
     * @param encoding Encoding
     * @throws ConfigurationException
     * @see FileConfiguration#load(InputStream,String)
     */
    @Override
    public void load(InputStream in, String encoding)
        throws ConfigurationException
    {
        if (userConfiguration == null) {
            throw new IllegalStateException("userConfiguration == null");
        }

        userConfiguration.load(in, encoding);
    }

    /**
     * Save.
     * 
     * @param out OutputStream
     * @param encoding Encoding
     * @throws ConfigurationException
     * @see FileConfiguration#save(OutputStream,String)
     */
    @Override
    public void save(OutputStream out, String encoding)
        throws ConfigurationException
    {
        if (userConfiguration == null) {
            throw new IllegalStateException("userConfiguration == null");
        }

        userConfiguration.save(out, encoding);
    }

    /**
     * Load.
     * 
     * @param in Reader
     * @throws ConfigurationException
     * @see FileConfiguration#load(Reader)
     */
    @Override
    public void load(Reader in)
        throws ConfigurationException
    {
        if (userConfiguration == null) {
            throw new IllegalStateException("userConfiguration == null");
        }

        userConfiguration.load(in);
    }

    /**
     * Save.
     * 
     * @param out Writer
     * @throws ConfigurationException
     * @see FileConfiguration#save(Writer)
     */
    @Override
    public void save(Writer out)
        throws ConfigurationException
    {
        if (userConfiguration == null) {
            throw new IllegalStateException("userConfiguration == null");
        }

        userConfiguration.save(out);
    }
}
